home *** CD-ROM | disk | FTP | other *** search
/ PC go! 2017 October / PCgo 10-2017 CD-ROM Germany.iso / nw.pak / Unnamed File 000121.txt < prev    next >
Encoding:
Text File  |  2015-07-29  |  9.5 KB  |  331 lines

  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4.  
  5. /**
  6.  * @fileoverview A command is an abstraction of an action a user can do in the
  7.  * UI.
  8.  *
  9.  * When the focus changes in the document for each command a canExecute event
  10.  * is dispatched on the active element. By listening to this event you can
  11.  * enable and disable the command by setting the event.canExecute property.
  12.  *
  13.  * When a command is executed a command event is dispatched on the active
  14.  * element. Note that you should stop the propagation after you have handled the
  15.  * command if there might be other command listeners higher up in the DOM tree.
  16.  */
  17.  
  18. cr.define('cr.ui', function() {
  19.  
  20.   /**
  21.    * This is used to identify keyboard shortcuts.
  22.    * @param {string} shortcut The text used to describe the keys for this
  23.    *     keyboard shortcut.
  24.    * @constructor
  25.    */
  26.   function KeyboardShortcut(shortcut) {
  27.     var mods = {};
  28.     var ident = '';
  29.     shortcut.split('-').forEach(function(part) {
  30.       var partLc = part.toLowerCase();
  31.       switch (partLc) {
  32.         case 'alt':
  33.         case 'ctrl':
  34.         case 'meta':
  35.         case 'shift':
  36.           mods[partLc + 'Key'] = true;
  37.           break;
  38.         default:
  39.           if (ident)
  40.             throw Error('Invalid shortcut');
  41.           ident = part;
  42.       }
  43.     });
  44.  
  45.     this.ident_ = ident;
  46.     this.mods_ = mods;
  47.   }
  48.  
  49.   KeyboardShortcut.prototype = {
  50.     /**
  51.      * Whether the keyboard shortcut object matches a keyboard event.
  52.      * @param {!Event} e The keyboard event object.
  53.      * @return {boolean} Whether we found a match or not.
  54.      */
  55.     matchesEvent: function(e) {
  56.       if (e.keyIdentifier == this.ident_) {
  57.         // All keyboard modifiers needs to match.
  58.         var mods = this.mods_;
  59.         return ['altKey', 'ctrlKey', 'metaKey', 'shiftKey'].every(function(k) {
  60.           return e[k] == !!mods[k];
  61.         });
  62.       }
  63.       return false;
  64.     }
  65.   };
  66.  
  67.   /**
  68.    * Creates a new command element.
  69.    * @constructor
  70.    * @extends {HTMLElement}
  71.    */
  72.   var Command = cr.ui.define('command');
  73.  
  74.   Command.prototype = {
  75.     __proto__: HTMLElement.prototype,
  76.  
  77.     /**
  78.      * Initializes the command.
  79.      */
  80.     decorate: function() {
  81.       CommandManager.init(assert(this.ownerDocument));
  82.  
  83.       if (this.hasAttribute('shortcut'))
  84.         this.shortcut = this.getAttribute('shortcut');
  85.     },
  86.  
  87.     /**
  88.      * Executes the command by dispatching a command event on the given element.
  89.      * If |element| isn't given, the active element is used instead.
  90.      * If the command is {@code disabled} this does nothing.
  91.      * @param {HTMLElement=} opt_element Optional element to dispatch event on.
  92.      */
  93.     execute: function(opt_element) {
  94.       if (this.disabled)
  95.         return;
  96.       var doc = this.ownerDocument;
  97.       if (doc.activeElement) {
  98.         var e = new Event('command', {bubbles: true});
  99.         e.command = this;
  100.  
  101.         (opt_element || doc.activeElement).dispatchEvent(e);
  102.       }
  103.     },
  104.  
  105.     /**
  106.      * Call this when there have been changes that might change whether the
  107.      * command can be executed or not.
  108.      * @param {Node=} opt_node Node for which to actuate command state.
  109.      */
  110.     canExecuteChange: function(opt_node) {
  111.       dispatchCanExecuteEvent(this,
  112.                               opt_node || this.ownerDocument.activeElement);
  113.     },
  114.  
  115.     /**
  116.      * The keyboard shortcut that triggers the command. This is a string
  117.      * consisting of a keyIdentifier (as reported by WebKit in keydown) as
  118.      * well as optional key modifiers joinded with a '-'.
  119.      *
  120.      * Multiple keyboard shortcuts can be provided by separating them by
  121.      * whitespace.
  122.      *
  123.      * For example:
  124.      *   "F1"
  125.      *   "U+0008-Meta" for Apple command backspace.
  126.      *   "U+0041-Ctrl" for Control A
  127.      *   "U+007F U+0008-Meta" for Delete and Command Backspace
  128.      *
  129.      * @type {string}
  130.      */
  131.     shortcut_: '',
  132.     get shortcut() {
  133.       return this.shortcut_;
  134.     },
  135.     set shortcut(shortcut) {
  136.       var oldShortcut = this.shortcut_;
  137.       if (shortcut !== oldShortcut) {
  138.         this.keyboardShortcuts_ = shortcut.split(/\s+/).map(function(shortcut) {
  139.           return new KeyboardShortcut(shortcut);
  140.         });
  141.  
  142.         // Set this after the keyboardShortcuts_ since that might throw.
  143.         this.shortcut_ = shortcut;
  144.         cr.dispatchPropertyChange(this, 'shortcut', this.shortcut_,
  145.                                   oldShortcut);
  146.       }
  147.     },
  148.  
  149.     /**
  150.      * Whether the event object matches the shortcut for this command.
  151.      * @param {!Event} e The key event object.
  152.      * @return {boolean} Whether it matched or not.
  153.      */
  154.     matchesEvent: function(e) {
  155.       if (!this.keyboardShortcuts_)
  156.         return false;
  157.  
  158.       return this.keyboardShortcuts_.some(function(keyboardShortcut) {
  159.         return keyboardShortcut.matchesEvent(e);
  160.       });
  161.     },
  162.   };
  163.  
  164.   /**
  165.    * The label of the command.
  166.    */
  167.   cr.defineProperty(Command, 'label', cr.PropertyKind.ATTR);
  168.  
  169.   /**
  170.    * Whether the command is disabled or not.
  171.    */
  172.   cr.defineProperty(Command, 'disabled', cr.PropertyKind.BOOL_ATTR);
  173.  
  174.   /**
  175.    * Whether the command is hidden or not.
  176.    */
  177.   cr.defineProperty(Command, 'hidden', cr.PropertyKind.BOOL_ATTR);
  178.  
  179.   /**
  180.    * Whether the command is checked or not.
  181.    */
  182.   cr.defineProperty(Command, 'checked', cr.PropertyKind.BOOL_ATTR);
  183.  
  184.   /**
  185.    * The flag that prevents the shortcut text from being displayed on menu.
  186.    *
  187.    * If false, the keyboard shortcut text (eg. "Ctrl+X" for the cut command)
  188.    * is displayed in menu when the command is assosiated with a menu item.
  189.    * Otherwise, no text is displayed.
  190.    */
  191.   cr.defineProperty(Command, 'hideShortcutText', cr.PropertyKind.BOOL_ATTR);
  192.  
  193.   /**
  194.    * Dispatches a canExecute event on the target.
  195.    * @param {!cr.ui.Command} command The command that we are testing for.
  196.    * @param {EventTarget} target The target element to dispatch the event on.
  197.    */
  198.   function dispatchCanExecuteEvent(command, target) {
  199.     var e = new CanExecuteEvent(command);
  200.     target.dispatchEvent(e);
  201.     command.disabled = !e.canExecute;
  202.   }
  203.  
  204.   /**
  205.    * The command managers for different documents.
  206.    */
  207.   var commandManagers = {};
  208.  
  209.   /**
  210.    * Keeps track of the focused element and updates the commands when the focus
  211.    * changes.
  212.    * @param {!Document} doc The document that we are managing the commands for.
  213.    * @constructor
  214.    */
  215.   function CommandManager(doc) {
  216.     doc.addEventListener('focus', this.handleFocus_.bind(this), true);
  217.     // Make sure we add the listener to the bubbling phase so that elements can
  218.     // prevent the command.
  219.     doc.addEventListener('keydown', this.handleKeyDown_.bind(this), false);
  220.   }
  221.  
  222.   /**
  223.    * Initializes a command manager for the document as needed.
  224.    * @param {!Document} doc The document to manage the commands for.
  225.    */
  226.   CommandManager.init = function(doc) {
  227.     var uid = cr.getUid(doc);
  228.     if (!(uid in commandManagers)) {
  229.       commandManagers[uid] = new CommandManager(doc);
  230.     }
  231.   };
  232.  
  233.   CommandManager.prototype = {
  234.  
  235.     /**
  236.      * Handles focus changes on the document.
  237.      * @param {Event} e The focus event object.
  238.      * @private
  239.      * @suppress {checkTypes}
  240.      * TODO(vitalyp): remove the suppression.
  241.      */
  242.     handleFocus_: function(e) {
  243.       var target = e.target;
  244.  
  245.       // Ignore focus on a menu button or command item.
  246.       if (target.menu || target.command)
  247.         return;
  248.  
  249.       var commands = Array.prototype.slice.call(
  250.           target.ownerDocument.querySelectorAll('command'));
  251.  
  252.       commands.forEach(function(command) {
  253.         dispatchCanExecuteEvent(command, target);
  254.       });
  255.     },
  256.  
  257.     /**
  258.      * Handles the keydown event and routes it to the right command.
  259.      * @param {!Event} e The keydown event.
  260.      */
  261.     handleKeyDown_: function(e) {
  262.       var target = e.target;
  263.       var commands = Array.prototype.slice.call(
  264.           target.ownerDocument.querySelectorAll('command'));
  265.  
  266.       for (var i = 0, command; command = commands[i]; i++) {
  267.         if (command.matchesEvent(e)) {
  268.           // When invoking a command via a shortcut, we have to manually check
  269.           // if it can be executed, since focus might not have been changed
  270.           // what would have updated the command's state.
  271.           command.canExecuteChange();
  272.  
  273.           if (!command.disabled) {
  274.             e.preventDefault();
  275.             // We do not want any other element to handle this.
  276.             e.stopPropagation();
  277.             command.execute();
  278.             return;
  279.           }
  280.         }
  281.       }
  282.     }
  283.   };
  284.  
  285.   /**
  286.    * The event type used for canExecute events.
  287.    * @param {!cr.ui.Command} command The command that we are evaluating.
  288.    * @extends {Event}
  289.    * @constructor
  290.    * @class
  291.    */
  292.   function CanExecuteEvent(command) {
  293.     var e = new Event('canExecute', {bubbles: true, cancelable: true});
  294.     e.__proto__ = CanExecuteEvent.prototype;
  295.     e.command = command;
  296.     return e;
  297.   }
  298.  
  299.   CanExecuteEvent.prototype = {
  300.     __proto__: Event.prototype,
  301.  
  302.     /**
  303.      * The current command
  304.      * @type {cr.ui.Command}
  305.      */
  306.     command: null,
  307.  
  308.     /**
  309.      * Whether the target can execute the command. Setting this also stops the
  310.      * propagation and prevents the default. Callers can tell if an event has
  311.      * been handled via |this.defaultPrevented|.
  312.      * @type {boolean}
  313.      */
  314.     canExecute_: false,
  315.     get canExecute() {
  316.       return this.canExecute_;
  317.     },
  318.     set canExecute(canExecute) {
  319.       this.canExecute_ = !!canExecute;
  320.       this.stopPropagation();
  321.       this.preventDefault();
  322.     }
  323.   };
  324.  
  325.   // Export
  326.   return {
  327.     Command: Command,
  328.     CanExecuteEvent: CanExecuteEvent
  329.   };
  330. });
  331.